using System;
using System.Collections.Generic;
using System.Text;

namespace Enyo.Reflection
{
	public class Type : System.Reflection.TypeDelegator
	{
		public Type(System.Type type, params System.Type[] generics)
			: base(new Type(type).MakeSpezialized(generics))
		{
		}

		public Type(System.Type type)
			: base(type)
		{
		}

		public virtual object CreateInstance()
		{
			return base.Assembly.CreateInstance(base.FullName);
		}

		public static object CreateInstance(System.Type type)
		{
			return new Type(type).CreateInstance();
		}

		public static System.Type[] GetDerived(System.Type baseType)
		{
			if (baseType.IsGenericType)
				baseType = baseType.GetGenericTypeDefinition();
			List<System.Type> derived = new List<System.Type>();
			foreach (System.Reflection.Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
			{
				if (!assembly.FullName.Contains("Enyo"))
					continue;
				System.Type[] types;
				try
				{
					types = assembly.GetTypes();
				}
				catch (System.Reflection.ReflectionTypeLoadException exc)
				{
					types = exc.Types;
				}
				System.Type type = null;
				foreach (System.Type temp in types)
				{
					type = temp;
					if (type.IsGenericType)
						type = type.GetGenericTypeDefinition();
					if (type != null && (type.IsSubclassOf(baseType) || IsDerived(baseType, type.BaseType)))
					{
						derived.Add(type);
					}
				}
			}
			return derived.ToArray();
		}

		protected static bool IsDerived(System.Type baseType, System.Type type)
		{
			if (object.ReferenceEquals(baseType, null) || baseType.Equals(typeof(object)) || object.ReferenceEquals(type, null) || type.Equals(typeof(object)))
				return false;
			if (type.IsGenericType)
				type = type.GetGenericTypeDefinition();
			if (baseType.IsInterface)
			{
				//TODO: implement interface checking...
				//return new List<Type>(type.GetInterfaces()).Contains(baseType);
			}
			if (baseType.Equals(type))
				return true;
			else
				return IsDerived(baseType, type.BaseType);
		}

		public System.Type[] GetDerived()
		{
			return GetDerived(this);
		}

		public virtual System.Type RuntimeType
		{
			get { return base.Assembly.GetType(this.FullName); }
		}

		public Type MakeSpezialized(params System.Type[] generics)
		{
			System.Type t = this.RuntimeType;
			t = t.MakeGenericType(generics);
			return new Type(t);
		}
	}
}
